export default class KeyCodeUtil {
  static isNumer(key: string): boolean {
    let num = parseInt(key)
    return !isNaN(num)
  }

  static isLeftArrow(key: string): boolean {
    return key === 'ArrowLeft'
  }

  static isRightArrow(key: string): boolean {
    return key === 'ArrowRight'
  }
}

export function to<T>(promise: Promise<T>) {
  return promise.then(data => [data, null]).catch(err => [null, err])
}

export function New1DArray<T>(n: number, fn: (idx: number) => T) {
  return new Array(n).fill(null).map((_, idx) => fn(idx))
}

export function New2DArray<T>(
  n: number,
  fn: (rowIdx: number, colIdx: number) => T
): T[][] {
  return new Array(n)
    .fill(null)
    .map((_, row) => new Array(n).fill(null).map((_, col) => fn(row, col)))
}

// Gurantee that the function will only be called once
export function callOnce(fn: () => void): () => void {
  let called = false
  return () => {
    if (called) return
    called = true
    fn()
  }
}

// limit call times of this function
export let limitCall = (maxCnt: number, fn: CallableFunction) => {
  let callCnt = 0
  return (arg: any) => {
    if (callCnt < maxCnt) {
      callCnt++
      fn(arg)
    }
  }
}

export function CallOnce() {
  return function innerDecorator(target: any, key: string, descriptor: any) {
    return {
      configurable: true,
      enumerable: descriptor.enumerable,
      get: function(): any {
        // Attach this function to the instance (not the class)
        Object.defineProperty(this, key, {
          configurable: true,
          enumerable: descriptor.enumerable,
          value: callOnce(descriptor.value.bind(this))
        })

        // @ts-ignore
        return this[key]
      }
    }
  }
}
